<script setup lang="ts">
import type {ZodTypeAny} from 'zod';

import type {
  FormCommonConfig,
  FormRenderProps,
  FormSchema,
  FormShape,
} from '../types';

import {computed} from 'vue';

import {Form} from '@/components/common/shadcn';
import {cn, isString, mergeWithArrayOverride} from '@/utils/shared/utils';

import {type GenericObject} from 'vee-validate';

import {provideFormRenderProps} from './context';
import {useExpandable} from './expandable';
import FormField from './form-field.vue';
import {getBaseRules, getDefaultValueInZodStack} from './helper';

interface Props extends FormRenderProps {
}

const props = withDefaults(
    defineProps<{ globalCommonConfig?: FormCommonConfig } & Props>(),
    {
      collapsedRows: 1,
      commonConfig: () => ({}),
      globalCommonConfig: () => ({}),
      showCollapseButton: false,
      wrapperClass: 'grid-cols-1 sm:grid-cols-2 md:grid-cols-3',
    },
);

const emits = defineEmits<{
  submit: [event: any];
}>();

provideFormRenderProps(props);

const {isCalculated, keepFormItemIndex, wrapperRef} = useExpandable(props);

const shapes = computed(() => {
  const resultShapes: FormShape[] = [];
  props.schema?.forEach((schema) => {
    const {fieldName} = schema;
    const rules = schema.rules as ZodTypeAny;

    let typeName = '';
    if (rules && !isString(rules)) {
      typeName = rules._def.typeName;
    }

    const baseRules = getBaseRules(rules) as ZodTypeAny;

    resultShapes.push({
      default: getDefaultValueInZodStack(rules),
      fieldName,
      required: !['ZodNullable', 'ZodOptional'].includes(typeName),
      rules: baseRules,
    });
  });
  return resultShapes;
});

const formComponent = computed(() => (props.form ? 'form' : Form));

const formComponentProps = computed(() => {
  return props.form
      ? {
        onSubmit: props.form.handleSubmit((val) => emits('submit', val)),
      }
      : {
        onSubmit: (val: GenericObject) => emits('submit', val),
      };
});

const formCollapsed = computed(() => {
  return props.collapsed && isCalculated.value;
});

const computedSchema = computed(
    (): ({
      commonComponentProps: Record<string, any>;
      formFieldProps: Record<string, any>;
    } & Omit<FormSchema, 'formFieldProps'>)[] => {
      const {
        colon = false,
        componentProps = {},
        controlClass = '',
        disabled,
        disabledOnChangeListener = true,
        disabledOnInputListener = true,
        emptyStateValue = undefined,
        formFieldProps = {},
        formItemClass = '',
        hideLabel = false,
        hideRequiredMark = false,
        labelClass = '',
        labelWidth = 100,
        wrapperClass = '',
      } = mergeWithArrayOverride(props.commonConfig, props.globalCommonConfig);
      return (props.schema || []).map((schema, index) => {
        const keepIndex = keepFormItemIndex.value;

        const hidden =
            // 折叠状态 & 显示折叠按钮 & 当前索引大于保留索引
            props.showCollapseButton && !!formCollapsed.value && keepIndex
                ? keepIndex <= index
                : false;

        return {
          colon,
          disabled,
          disabledOnChangeListener,
          disabledOnInputListener,
          emptyStateValue,
          hideLabel,
          hideRequiredMark,
          labelWidth,
          wrapperClass,
          ...schema,
          commonComponentProps: componentProps,
          componentProps: schema.componentProps,
          controlClass: cn(controlClass, schema.controlClass),
          formFieldProps: {
            ...formFieldProps,
            ...schema.formFieldProps,
          },
          formItemClass: cn(
              'flex-shrink-0',
              {hidden},
              formItemClass,
              schema.formItemClass,
          ),
          labelClass: cn(labelClass, schema.labelClass),
        };
      });
    },
);
</script>

<template>
  <component :is="formComponent" v-bind="formComponentProps">
    <div ref="wrapperRef" :class="wrapperClass" class="grid">
      <template v-for="cSchema in computedSchema" :key="cSchema.fieldName">
        <!-- <div v-if="$slots[cSchema.fieldName]" :class="cSchema.formItemClass">
          <slot :definition="cSchema" :name="cSchema.fieldName"> </slot>
        </div> -->
        <FormField
            v-bind="cSchema"
            :class="cSchema.formItemClass"
            :rules="cSchema.rules"
        >
          <template #default="slotProps">
            <slot v-bind="slotProps" :name="cSchema.fieldName"></slot>
          </template>
        </FormField>
      </template>
      <slot :shapes="shapes"></slot>
    </div>
  </component>
</template>
